package com.byox.drawview.views;

import com.byox.drawview.ResourceTable;
import com.byox.drawview.dictionaries.DrawMove;
import com.byox.drawview.enums.BackgroundScale;
import com.byox.drawview.enums.BackgroundType;
import com.byox.drawview.enums.DrawingCapture;
import com.byox.drawview.enums.DrawingMode;
import com.byox.drawview.enums.DrawingTool;
import com.byox.drawview.utils.AttrValue;
import com.byox.drawview.utils.MatrixUtils;
import com.byox.drawview.utils.PixelMapFactoryUtil;
import com.byox.drawview.utils.ScaleGestureDetector;
import com.byox.drawview.utils.SerializablePaint;
import com.byox.drawview.utils.SerializablePath;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.StackLayout;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.TouchEvent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Ing. Oscar G. Medina Cruz on 06/11/2016.
 * <p>
 * This view was created for draw or paint anything you want.
 * <p>
 * <p>
 * This view can be configurated for change draw color, width size, can use tools like pen, line, circle, square.
 * </p>
 *
 * @author Ing. Oscar G. Medina Cruz
 */
public class DrawView extends StackLayout implements Component.TouchEventListener, Component.DrawTask {

    // LISTENER
    private OnDrawViewListener onDrawViewListener;
    private ScaleGestureDetector mScaleGestureDetector;
//    private GestureDetector mGestureDetector;

    // VARS
    private boolean isForCamera = false;

    private int mDrawColor;
    private int mDrawWidth;
    private float mDrawAlpha;
    private boolean mAntiAlias;
    private boolean mDither;
    private SerializablePaint.Style mPaintStyle;
    private SerializablePaint.StrokeCap mLineCap;
    private Font mFontFamily;
    private int mFontSize;
    private int mBackgroundColor = -1;
    //private Object mBackgroundImage;
    private Rect mCanvasClipBounds;

    private PixelMap mContentBitmap;
    private PixelMap mBufferBitmap;
    private Canvas mContentCanvas;
    private Canvas mBufferCanvas;
    private Component mDrawView;
    private Component mBufferView;

    private boolean mZoomEnabled = false;
    private float mZoomFactor = 1.0f;
    private float mZoomCenterX = -1.0f;
    private float mZoomCenterY = -1.0f;
    private float mMaxZoomFactor = 8f;
    private float mZoomRegionScale = 4f;
    private float mZoomRegionScaleMin = 2f;
    private float mZoomRegionScaleMax = 5f;
    private boolean mFromZoomRegion = false;

    private int mLastTouchEvent = -1;
    private int touchCount = 0;
    private long downTime;

    private DrawingMode mDrawingMode;
    private DrawingTool mDrawingTool;
    //private DrawingOrientation mInitialDrawingOrientation;

    private List<DrawMove> mDrawMoveHistory;// 路径记录
    private int mDrawMoveHistoryIndex = -1;// 历史路径index
    private int mDrawMoveBackgroundIndex = -1;// background index

    private RectFloat mAuxRect;
    private BlendMode mEraserXefferMode;
    private SerializablePaint mBackgroundPaint;

    //private Rect mInvalidateRect;

    private DrawMove currentDrawMove;

    // VIEWS
//    private CardView mZoomRegionCardView;
    private ZoomRegionView mZoomRegionView;

    private boolean isDoubleEvent = false;
    private boolean isNeedUpdate = false;
    private boolean historySwitch = true;// true 开启绘制历史；false 关闭

    /**
     * Default constructor
     *
     * @param context
     */
    public DrawView(Context context) {
        super(context);
        initVars();
    }

    /**
     * Default constructor
     *
     * @param context
     * @param attrs
     */
    public DrawView(Context context, AttrSet attrs) {
        super(context, attrs);
        initVars();
        initAttributes(context, attrs);
    }

    /**
     * Default constructor
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public DrawView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initVars();
        initAttributes(context, attrs);
    }


    /**
     * 绘制方法
     *
     * @param component 组件对象
     * @param canvas 画布对象
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        initZoomRegionView();

        if(!isDoubleEvent || mContentBitmap == null) {
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.size = new Size(getWidth(), getHeight());
            mContentBitmap = PixelMap.create(options);
            Texture texture = new Texture(mContentBitmap);
            mContentCanvas = new Canvas(texture);
        }
        if (isZoomEnabled()) {
            canvas.save();
            canvas.scale(mZoomFactor, mZoomFactor, mZoomCenterX, mZoomCenterY);
        }
        if (mDrawMoveHistory == null) {
            return;
        }
        if(mDrawMoveHistory.size() == 0 || isNeedUpdate) {
            mContentCanvas.drawColor(0, BlendMode.CLEAR);
            isNeedUpdate = false;
        }

        if(!isDoubleEvent) {
            if (mDrawMoveBackgroundIndex != -1 && mDrawMoveHistory.size() > 0) {
                DrawMove drawMove = mDrawMoveHistory.get(mDrawMoveBackgroundIndex);
                drawBackgroundImage(drawMove, mContentCanvas);
            }

            for (int i = 0; i < mDrawMoveHistoryIndex + 1; i++) {
                DrawMove drawMove = mDrawMoveHistory.get(i);
                drawContent(mContentCanvas, drawMove);
                if (i == mDrawMoveHistory.size() - 1 && onDrawViewListener != null)
                    onDrawViewListener.onAllMovesPainted();
            }

        }
        canvas.getLocalClipBounds(mCanvasClipBounds);
        mCanvasClipBounds.right = (int) (canvas.getLocalClipBounds().left + getWidth()/(1.0f * mZoomFactor));
        mCanvasClipBounds.bottom = (int) (canvas.getLocalClipBounds().top + getHeight()/(1.0f * mZoomFactor));
        canvas.drawPixelMapHolder(new PixelMapHolder(mContentBitmap), 0, 0, new Paint());
        if (isZoomEnabled()) {

            if (mZoomRegionView != null) {

                if(mFromZoomRegion){
                    mZoomRegionView.updateParent(mContentBitmap);
                }
                else{
                    mZoomRegionView.drawZoomRegion(mContentBitmap, mCanvasClipBounds, 4);
                }
            }
            canvas.restore();
        }
    }

    /**
     * drawContent方法
     *
     * @param canvas 参数
     * @param drawMove 参数
     */
    public void drawContent(Canvas canvas, DrawMove drawMove){
        if (drawMove.getDrawingMode() != null) {
            switch (drawMove.getDrawingMode()) {
                case DRAW:
                    switch (drawMove.getDrawingTool()) {
                        case PEN:
                            if (drawMove.getDrawingPath() != null)
                                canvas.drawPath(drawMove.getDrawingPath(), drawMove.getPaint());
                            break;
                        case LINE:
                            canvas.drawLine(drawMove.getStartX(), drawMove.getStartY(),
                                    drawMove.getEndX(), drawMove.getEndY(), drawMove.getPaint());
                            break;
                        case ARROW:
                            canvas.drawLine(drawMove.getStartX(), drawMove.getStartY(),
                                    drawMove.getEndX(), drawMove.getEndY(), drawMove.getPaint());
                            float angle = (float) Math.toDegrees(Math.atan2(drawMove.getEndY() - drawMove.getStartY(),
                                    drawMove.getEndX() - drawMove.getStartX())) - 90;
                            angle = angle < 0 ? angle + 360 : angle;
                            float middleWidth = 8f + drawMove.getPaint().getStrokeWidth();
                            float arrowHeadLarge = 30f + drawMove.getPaint().getStrokeWidth();

                            canvas.save();
                            canvas.translate(drawMove.getEndX(), drawMove.getEndY());
                            canvas.rotate(angle);
                            canvas.drawLine(0f, 0f, middleWidth, 0f, drawMove.getPaint());
                            canvas.drawLine(middleWidth, 0f, 0f, arrowHeadLarge, drawMove.getPaint());
                            canvas.drawLine(0f, arrowHeadLarge, -middleWidth, 0f, drawMove.getPaint());
                            canvas.drawLine(-middleWidth, 0f, 0f, 0f, drawMove.getPaint());
                            canvas.restore();

                            break;
                        case RECTANGLE:
                            float left = Math.min(drawMove.getStartX(), drawMove.getEndX());
                            float right = Math.max(drawMove.getStartX(), drawMove.getEndX());
                            float top = Math.min( drawMove.getStartY(), drawMove.getEndY());
                            float bottom = Math.max( drawMove.getStartY(), drawMove.getEndY());
                            canvas.drawRect(left, top,
                                    right, bottom, drawMove.getPaint());
                            break;
                        case CIRCLE:
                            if (drawMove.getEndX() > drawMove.getStartX()) {
                                canvas.drawCircle(drawMove.getStartX(), drawMove.getStartY(),
                                        drawMove.getEndX() - drawMove.getStartX(), drawMove.getPaint());
                            } else {
                                canvas.drawCircle(drawMove.getStartX(), drawMove.getStartY(),
                                        drawMove.getStartX() - drawMove.getEndX(), drawMove.getPaint());
                            }
                            break;
                        case ELLIPSE:
                            setRect(mAuxRect,
                                    drawMove.getEndX() - Math.abs(drawMove.getEndX() - drawMove.getStartX()),
                                    drawMove.getEndY() - Math.abs(drawMove.getEndY() - drawMove.getStartY()),
                                    drawMove.getEndX() + Math.abs(drawMove.getEndX() - drawMove.getStartX()),
                                    drawMove.getEndY() + Math.abs(drawMove.getEndY() - drawMove.getStartY()));
                            canvas.drawOval(mAuxRect, drawMove.getPaint());
                            break;
                    }
                    break;
                case TEXT:
                    if (drawMove.getText() != null && !drawMove.getText().equals("")) {
                        canvas.drawText(drawMove.getPaint(), drawMove.getText(), drawMove.getEndX(), drawMove.getEndY());
                    }
                    break;
                case ERASER:
                    if (drawMove.getDrawingPath() != null) {
                        drawMove.getPaint().setBlendMode(mEraserXefferMode);
                        canvas.drawPath(drawMove.getDrawingPath(), drawMove.getPaint());
                        drawMove.getPaint().setBlendMode(null);
                    }
                    break;
            }
        }
    }

    /**
     * onBufferDraw方法
     *
     * @param component 组件参数
     * @param canvas 参数
     */
    public void onBufferDraw(Component component, Canvas canvas) {

        if(mBufferBitmap == null){
            initBufferView();
        }

        if (isZoomEnabled()) {
            canvas.save();
            canvas.scale(mZoomFactor, mZoomFactor, mZoomCenterX, mZoomCenterY);
        }

        mBufferCanvas.drawColor(0, BlendMode.CLEAR);

        if(mDrawMoveHistory.size() > 0 && currentDrawMove != null){
            drawContent(mBufferCanvas, currentDrawMove);
        }

        canvas.drawPixelMapHolder(new PixelMapHolder(mBufferBitmap), 0, 0, new Paint());

        if (isZoomEnabled()) {
            canvas.restore();
        }

    }

    private void setRect(RectFloat rectFloat, float left, float top, float right, float bottom){
        rectFloat.left = left;
        rectFloat.top = top;
        rectFloat.right = right;
        rectFloat.bottom = bottom;
    }

    // PRIVATE METHODS

    /**
     * Initialize general vars for the view
     */
    private void initVars() {
        mDrawMoveHistory = new ArrayList<>();
        mCanvasClipBounds = new Rect();
        mAuxRect = new RectFloat();
        mEraserXefferMode = BlendMode.CLEAR;
        mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureListener());
        setTouchEventListener(this);
    }

    /**
     * Init the ZoomRegionView for navigate into image when user zoom in
     */
    private void initZoomRegionView() {
        if (mZoomRegionView == null) {
            StackLayout.LayoutConfig layoutParams = new LayoutConfig(getWidth() / 4, getHeight() / 4, LayoutAlignment.RIGHT);
            layoutParams.setMargins(12, 12, 12, 12);
            mZoomRegionView = new ZoomRegionView(getContext());
            mZoomRegionView.setLayoutConfig(layoutParams);
            mZoomRegionView.setScaleMode(Image.ScaleMode.CENTER);
            mZoomRegionView.setOnZoomRegionListener(new ZoomRegionView.OnZoomRegionListener() {
                @Override
                public void onZoomRegionMoved(RectFloat newRect) {
                    mFromZoomRegion = true;
                    mZoomCenterX = newRect.getCenter().getPointX() * 4;
                    mZoomCenterY = newRect.getCenter().getPointY() * 4;
                    mDrawView.invalidate();
                }
            });

            if(mZoomCenterX == -1){
                mZoomCenterX = getWidth()/2f;
                mZoomCenterY = getHeight()/2f;
            }
            mZoomRegionView.setVisibility(INVISIBLE);
            addComponent(mZoomRegionView);
        }
    }

    private void initBufferView(){
        PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
        options.size = new Size(getWidth(), getHeight());
        mBufferBitmap = PixelMap.create(options);
        Texture texture = new Texture(mBufferBitmap);
        mBufferCanvas = new Canvas(texture);
    }

    /**
     * Initialize view attributes
     *
     * @param context
     * @param attrs
     */
    private void initAttributes(Context context, AttrSet attrs) {
        mDrawColor = AttrValue.get(attrs, "dv_draw_color", Color.BLACK).getValue();
        mDrawWidth = AttrValue.get(attrs, "dv_draw_width", 3);
        mDrawAlpha = AttrValue.get(attrs, "dv_draw_alpha", 255)/255.0F;
        mAntiAlias = AttrValue.get(attrs, "dv_draw_anti_alias", true);
        mDither = AttrValue.get(attrs, "dv_draw_dither", true);
        int paintStyle = AttrValue.get(attrs, "dv_draw_style", 2);
        if (paintStyle == 0)
            mPaintStyle = SerializablePaint.Style.FILL_STYLE;
        else if (paintStyle == 1)
            mPaintStyle = SerializablePaint.Style.FILLANDSTROKE_STYLE;
        else if (paintStyle == 2)
            mPaintStyle = SerializablePaint.Style.STROKE_STYLE;
        int cap = AttrValue.get(attrs, "dv_draw_corners", 1);
        if (cap == 0)
            mLineCap = SerializablePaint.StrokeCap.BUTT_CAP;
        else if (cap == 1)
            mLineCap = SerializablePaint.StrokeCap.ROUND_CAP;
        else if (cap == 2)
            mLineCap = SerializablePaint.StrokeCap.SQUARE_CAP;
        int typeface = AttrValue.get(attrs, "dv_draw_font_family", 0);
        if (typeface == 0)
            mFontFamily = Font.DEFAULT;
        else if (typeface == 1)
            mFontFamily = Font.MONOSPACE;
        else if (typeface == 2)
            mFontFamily = Font.SANS_SERIF;
        else if (typeface == 3)
            mFontFamily = Font.SERIF;
        mFontSize = AttrValue.get(attrs, "dv_draw_font_size", 12);
        isForCamera = AttrValue.get(attrs, "dv_draw_is_camera", false);
        AttrValue.get(attrs, "dv_draw_orientation",
                getWidth() > getHeight() ? 1 : 0);
        if (!isForCamera){
            try {
                PixelMap pixelMap = PixelMapFactoryUtil.getPixelMapFromResource(getContext(), ResourceTable.Media_bg, new ImageSource.DecodingOptions());
                PixelMapElement pixelMapElement = new PixelMapElement(pixelMap);
                this.setBackground(pixelMapElement);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        mBackgroundPaint = new SerializablePaint();
        mBackgroundPaint.setStyle(SerializablePaint.Style.FILL_STYLE);
        mBackgroundPaint.setColor(Color.TRANSPARENT);//mBackgroundColor != -1 ? mBackgroundColor : Color.TRANSPARENT);

        mDrawingTool = DrawingTool.values()[0];
        mDrawingMode = DrawingMode.values()[0];

        mZoomEnabled = AttrValue.get(attrs, "dv_draw_enable_zoom", false);
        mZoomRegionScale = AttrValue.get(attrs, "dv_draw_zoomregion_scale", mZoomRegionScale);
        mZoomRegionScaleMin = AttrValue.get(attrs, "dv_draw_zoomregion_minscale", mZoomRegionScaleMin);
        mZoomRegionScaleMax = AttrValue.get(attrs, "dv_draw_zoomregion_maxscale", mZoomRegionScaleMax);

        mDrawView = new Component(getContext(), attrs);
        mDrawView.addDrawTask(this::onDraw);
        addComponent(mDrawView);

        mBufferView = new Component(getContext(), attrs);
        mBufferView.addDrawTask(this::onBufferDraw);
        mBufferView.setTouchEventListener(this::onBufferTouchEvent);
        addComponent(mBufferView);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {

        if (!historySwitch) {
            return false;
        }

        if (mZoomEnabled) {
            zoomLogic(component, touchEvent);
        }

        float touchX = touchEvent.getPointerPosition(0).getX() / mZoomFactor + mCanvasClipBounds.left;
        float touchY = touchEvent.getPointerPosition(0).getY() / mZoomFactor + mCanvasClipBounds.top;

        int lastMoveIndex = 0;

        if (touchEvent.getPointerCount() == 1) {
            switch (touchEvent.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    mLastTouchEvent = TouchEvent.PRIMARY_POINT_DOWN;

                    if (onDrawViewListener != null)
                        onDrawViewListener.onStartDrawing();

                    if (mDrawMoveHistoryIndex >= -1 &&
                            mDrawMoveHistoryIndex < mDrawMoveHistory.size() - 1)
                        mDrawMoveHistory = mDrawMoveHistory.subList(0, mDrawMoveHistoryIndex + 1);

                    mDrawMoveHistory.add(DrawMove.newInstance()
                            .setPaint(getNewPaintParams())
                            .setStartX(touchX).setStartY(touchY)
                            .setEndX(touchX).setEndY(touchY)
                            .setDrawingMode(mDrawingMode).setDrawingTool(mDrawingTool));
                    lastMoveIndex = mDrawMoveHistory.size() - 1;

                    mDrawMoveHistoryIndex++;

                    if (mDrawingTool == DrawingTool.PEN || mDrawingMode == DrawingMode.ERASER) {
                        SerializablePath path = new SerializablePath();
                        path.moveTo(touchX, touchY);
                        path.lineTo(touchX, touchY);

                        mDrawMoveHistory.get(lastMoveIndex).setDrawingPathList(path);
                    }
                    break;
                case TouchEvent.POINT_MOVE:
                    if ((mLastTouchEvent == TouchEvent.PRIMARY_POINT_DOWN ||
                            mLastTouchEvent == TouchEvent.POINT_MOVE)) {
                        mLastTouchEvent = TouchEvent.POINT_MOVE;

                        lastMoveIndex = mDrawMoveHistory.size() - 1;

                        if (mDrawMoveHistory.size() > 0) {
                            mDrawMoveHistory.get(lastMoveIndex).setEndX(touchX).setEndY(touchY);

                            if (mDrawingTool == DrawingTool.PEN || mDrawingMode == DrawingMode.ERASER) {
                                mDrawMoveHistory.get(lastMoveIndex).getDrawingPath().lineTo(touchX, touchY);
                            }
                        }
                    }
                    break;
                case TouchEvent.PRIMARY_POINT_UP:
                    lastMoveIndex = mDrawMoveHistory.size() - 1;

                    if (mLastTouchEvent == TouchEvent.PRIMARY_POINT_DOWN) {
                        if (mDrawMoveHistory.size() > 0) {
                            mDrawMoveHistory.remove(lastMoveIndex);
                            mDrawMoveHistoryIndex--;
                            lastMoveIndex--;
                        }
                    } else if (mLastTouchEvent == TouchEvent.POINT_MOVE) {
                        mLastTouchEvent = -1;
                        if (mDrawMoveHistory.size() > 0) {
                            mDrawMoveHistory.get(lastMoveIndex).setEndX(touchX).setEndY(touchY);

                            if (mDrawingTool == DrawingTool.PEN || mDrawingMode == DrawingMode.ERASER) {
                                mDrawMoveHistory.get(lastMoveIndex).getDrawingPath().lineTo(touchX, touchY);
                            }
                        }
                    }

                    if (onDrawViewListener != null && mDrawingMode == DrawingMode.TEXT)
                        onDrawViewListener.onRequestText();

                    if (onDrawViewListener != null)
                        onDrawViewListener.onEndDrawing();

                    break;
                default:
                    return false;
            }
        } else {
            mLastTouchEvent = -1;
        }

        mDrawView.invalidate();
        return true;
    }

    /**
     * zoomLogic方法
     *
     * @param component 组件对象
     * @param touchEvent 按下事件
     */
    public void zoomLogic(Component component, TouchEvent touchEvent){
        mScaleGestureDetector.onTouchEvent(component, touchEvent);
//            mGestureDetector.onTouchEvent(motionEvent);
        if(touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            if(touchCount == 0 || System.currentTimeMillis() - downTime > 300) {
                downTime = System.currentTimeMillis();
                touchCount = 1;
            }
            else{
                touchCount = 2;
            }
        }
        if(touchCount == 2){
            if(touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP && System.currentTimeMillis() - downTime <= 300){
                onDoubleTap(touchEvent);
            }
            if(touchEvent.getAction() == TouchEvent.CANCEL || touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP){
                touchCount = 0;
            }
        }
    }

    /**
     * onBufferTouchEvent方法
     *
     * @param component 组件对象
     * @param touchEvent 按键事件
     * @return false boolean类型
     */
    public boolean onBufferTouchEvent(Component component, TouchEvent touchEvent){

        if (mDrawingTool == DrawingTool.PEN || mDrawingMode == DrawingMode.ERASER) {
            return false;
        }

        if (!historySwitch) {
            return false;
        }

        if (mZoomEnabled) {
            zoomLogic(component, touchEvent);
        }

        float touchX = touchEvent.getPointerPosition(0).getX() / mZoomFactor + mCanvasClipBounds.left;
        float touchY = touchEvent.getPointerPosition(0).getY() / mZoomFactor + mCanvasClipBounds.top;

        int lastMoveIndex = 0;

        if (touchEvent.getPointerCount() == 1) {
            switch (touchEvent.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    mLastTouchEvent = TouchEvent.PRIMARY_POINT_DOWN;

                    if (onDrawViewListener != null)
                        onDrawViewListener.onStartDrawing();

                    if (mDrawMoveHistoryIndex >= -1 &&
                            mDrawMoveHistoryIndex < mDrawMoveHistory.size() - 1)
                        mDrawMoveHistory = mDrawMoveHistory.subList(0, mDrawMoveHistoryIndex + 1);

                    currentDrawMove = DrawMove.newInstance()
                            .setPaint(getNewPaintParams())
                            .setStartX(touchX).setStartY(touchY)
                            .setEndX(touchX).setEndY(touchY)
                            .setDrawingMode(mDrawingMode).setDrawingTool(mDrawingTool);
                    mDrawMoveHistory.add(currentDrawMove);
                    lastMoveIndex = mDrawMoveHistory.size() - 1;

                    mDrawMoveHistoryIndex++;

                    if (mDrawingTool == DrawingTool.PEN || mDrawingMode == DrawingMode.ERASER) {
                        SerializablePath path = new SerializablePath();
                        path.moveTo(touchX, touchY);
                        path.lineTo(touchX, touchY);

                        mDrawMoveHistory.get(lastMoveIndex).setDrawingPathList(path);
                    }
                    break;
                case TouchEvent.POINT_MOVE:
                    if ((mLastTouchEvent == TouchEvent.PRIMARY_POINT_DOWN ||
                            mLastTouchEvent == TouchEvent.POINT_MOVE)) {
                        mLastTouchEvent = TouchEvent.POINT_MOVE;

                        lastMoveIndex = mDrawMoveHistory.size() - 1;

                        if (mDrawMoveHistory.size() > 0) {
                            mDrawMoveHistory.get(lastMoveIndex).setEndX(touchX).setEndY(touchY);

                            if (mDrawingTool == DrawingTool.PEN || mDrawingMode == DrawingMode.ERASER) {
                                mDrawMoveHistory.get(lastMoveIndex).getDrawingPath().lineTo(touchX, touchY);
                            }
                        }
                    }
                    break;
                case TouchEvent.PRIMARY_POINT_UP:
                    lastMoveIndex = mDrawMoveHistory.size() - 1;
                    if (mLastTouchEvent == TouchEvent.PRIMARY_POINT_DOWN) {
                        if (mDrawMoveHistory.size() > 0) {
                            mDrawMoveHistory.remove(lastMoveIndex);
                            mDrawMoveHistoryIndex--;
                            lastMoveIndex--;
                        }
                    } else if (mLastTouchEvent == TouchEvent.POINT_MOVE) {
                        mLastTouchEvent = -1;
                        if (mDrawMoveHistory.size() > 0) {
                            mDrawMoveHistory.get(lastMoveIndex).setEndX(touchX).setEndY(touchY);
                        }
                    }

                    if (onDrawViewListener != null && mDrawingMode == DrawingMode.TEXT)
                        onDrawViewListener.onRequestText();

                    if (onDrawViewListener != null)
                        onDrawViewListener.onEndDrawing();

                    currentDrawMove = null;
                    mBufferView.invalidate();
                    mDrawView.invalidate();
                    break;
                default:
                    return true;
            }
        } else {
            mLastTouchEvent = -1;
        }


        mBufferView.invalidate();
        return true;

    }
    /**
     * New paint parameters
     *
     * @return new paint parameters for initialize drawing
     */
    private SerializablePaint getNewPaintParams() {
        SerializablePaint paint = new SerializablePaint();

        if (mDrawingMode == DrawingMode.ERASER) {
            if (mDrawingTool != DrawingTool.PEN) {
                mDrawingTool = DrawingTool.PEN;
            }
            paint.setColor(new Color(mBackgroundColor));
        } else {
            paint.setColor(new Color(mDrawColor));
        }

        paint.setStyle(mPaintStyle);
        paint.setDither(mDither);
        paint.setStrokeWidth(mDrawWidth);
        paint.setAlpha(mDrawAlpha);
        paint.setAntiAlias(mAntiAlias);
        paint.setStrokeCap(mLineCap);
        paint.setFont(mFontFamily);
        paint.setTextSize(mFontSize);
        return paint;
    }

    // PUBLIC METHODS

    /**
     * Current paint parameters
     *
     * @return current paint parameters
     */
    public SerializablePaint getCurrentPaintParams() {
        SerializablePaint currentPaint;
        if (mDrawMoveHistory.size() > 0 && mDrawMoveHistoryIndex >= 0) {
            currentPaint = new SerializablePaint();
            currentPaint.setColor(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getColor());
            currentPaint.setStyle(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getStyle());
            currentPaint.setDither(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getDither());
            currentPaint.setStrokeWidth(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getStrokeWidth());
            currentPaint.setAlpha(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getAlpha());
            currentPaint.setAntiAlias(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().isAntiAlias());
            currentPaint.setStrokeCap(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getStrokeCap());
            currentPaint.setFont(
                    mDrawMoveHistory.get(mDrawMoveHistoryIndex).getPaint().getFont());
            currentPaint.setTextSize(mFontSize);
        } else {
            currentPaint = new SerializablePaint();
            currentPaint.setColor(new Color(mDrawColor));
            currentPaint.setStyle(mPaintStyle);
            currentPaint.setDither(mDither);
            currentPaint.setStrokeWidth(mDrawWidth);
            currentPaint.setAlpha(mDrawAlpha);
            currentPaint.setAntiAlias(mAntiAlias);
            currentPaint.setStrokeCap(mLineCap);
            currentPaint.setFont(mFontFamily);
            currentPaint.setTextSize(24);
        }
        return currentPaint;
    }

    /**
     * Restart all the parameters and drawing history
     *
     * @return if the draw view can be restarted
     */
    public boolean restartDrawing() {
        if (mDrawMoveHistory != null) {
            mDrawMoveHistory.clear();
            mDrawMoveHistoryIndex = -1;
            mDrawMoveBackgroundIndex = -1;
            mDrawView.invalidate();
            mBufferView.invalidate();
            if (onDrawViewListener != null)
                onDrawViewListener.onClearDrawing();

            return true;
        }
        mBufferView.invalidate();
        mDrawView.invalidate();
        return false;
    }

    /**
     * 清空绘制记录，不清空背景图片
     *
     * @return 是否清除成功
     */
    public boolean clearHistory() {
        if (mDrawMoveHistory != null) {
            if (mDrawMoveBackgroundIndex != -1) {
                DrawMove drawMove = mDrawMoveHistory.get(mDrawMoveBackgroundIndex);
                mDrawMoveHistory.clear();
                mDrawMoveHistory.add(drawMove);
                mDrawMoveHistoryIndex = 0;
                mDrawMoveBackgroundIndex = 0;
                mDrawView.invalidate();
            } else {
                mDrawMoveHistory.clear();
                mDrawMoveHistoryIndex = -1;
                mDrawMoveBackgroundIndex = -1;
                mDrawView.invalidate();
            }
            if (onDrawViewListener != null)
                onDrawViewListener.onClearDrawing();
            return true;
        }
        mDrawView.invalidate();
        return false;
    }

    /**
     * 绘制记录历史开关
     *
     * @param historySwitch true
     */
    public void setHistorySwitch(boolean historySwitch) {
        this.historySwitch = historySwitch;
    }

    /**
     * 返回绘制历史记录状态
     *
     * @return 是否成功
     */
    public boolean getHistorySwitch() {
        return this.historySwitch;
    }

    /**
     * Undo last drawing action
     *
     * @return if the view can do the undo action
     */
    public boolean undo() {

        if (mDrawMoveHistoryIndex > -1 &&
                mDrawMoveHistory.size() > 0) {
            mDrawMoveHistoryIndex--;

            mDrawMoveBackgroundIndex = -1;
            for (int i = 0; i < mDrawMoveHistoryIndex + 1; i++) {
                if (mDrawMoveHistory.get(i).getBackgroundImage() != null) {
                    mDrawMoveBackgroundIndex = i;
                }
            }

            mDrawView.invalidate();
            mBufferView.invalidate();
            isNeedUpdate = true;
            return true;
        }
        mBufferView.invalidate();
        mDrawView.invalidate();
        return false;
    }

    /**
     * Check if the draw view can do undo action
     *
     * @return if the view can do the undo action
     */
    public boolean canUndo() {
        return mDrawMoveHistoryIndex > -1 &&
                mDrawMoveHistory.size() > 0;
    }

    /**
     * Redo preview action
     *
     * @return if the view can do the redo action
     */
    public boolean redo() {
        if (mDrawMoveHistoryIndex <= mDrawMoveHistory.size() - 1) {
            mDrawMoveHistoryIndex++;
            mDrawMoveBackgroundIndex = -1;
            for (int i = 0; i < mDrawMoveHistoryIndex + 1; i++) {
                if (mDrawMoveHistory.get(i).getBackgroundImage() != null) {
                    mDrawMoveBackgroundIndex = i;
                }
            }
            isNeedUpdate = true;
            mBufferView.invalidate();
            mDrawView.invalidate();
            return true;
        }
        mBufferView.invalidate();
        mDrawView.invalidate();
        return false;
    }

    /**
     * Check if the view can do the redo action
     *
     * @return if the view can do the redo action
     */
    public boolean canRedo() {
        return mDrawMoveHistoryIndex < mDrawMoveHistory.size() - 1;
    }

    /**
     * Create capture of the drawing view as bitmap or as byte array
     *
     * @param drawingCapture
     * @return Object in form of bitmap or byte array
     */
    public Object[] createCapture(DrawingCapture drawingCapture) {
        Object[] result = null;
        switch (drawingCapture) {
            case BITMAP:
                result = new Object[2];
                result[0] = mContentBitmap;
                result[1] = mBackgroundPaint.getColor() == Color.TRANSPARENT ? "PNG" : "JPG";
                break;
            case BYTES:
//                result = new Object[2];
//                ByteArrayOutputStream stream = new ByteArrayOutputStream();
//                mContentBitmap.compress(
//                        mBackgroundPaint.getColor() == Color.TRANSPARENT ?
//                                Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG,
//                        100, stream);
//                result[0] = stream.toByteArray();
//                result[1] = mBackgroundPaint.getColor() == Color.TRANSPARENT ? "PNG" : "JPG";
                break;
        }
        return result;
    }

//    public Object[] createCapture(DrawingCapture drawingCapture, CameraView cameraView) {
//        Object[] result = null;
//        switch (drawingCapture) {
//            case BITMAP:
//                result = new Object[2];
//                PixelMap cameraBitmap = (PixelMap) cameraView.getCameraFrame(drawingCapture);
//                result[0] = BitmapUtils.GetCombinedBitmaps(cameraBitmap, mContentBitmap,
//                        mContentBitmap.getImageInfo().size.width, mContentBitmap.getImageInfo().size.height);
//                cameraBitmap.recycle();
//                result[1] = "JPG";
//                break;
//            case BYTES:
//                result = new Object[2];
//                ByteArrayOutputStream stream = new ByteArrayOutputStream();
//                byte[] cameraBytes = (byte[]) cameraView.getCameraFrame(drawingCapture);
//                cameraBitmap = BitmapFactory.decodeByteArray(cameraBytes, 0, cameraBytes.length);
//                PixelMap resultBitmap = BitmapUtils.GetCombinedBitmaps(cameraBitmap, mContentBitmap,
//                        mContentBitmap.getImageInfo().size.width, mContentBitmap.getImageInfo().size.height);
//                resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
//                resultBitmap.recycle();
//                result[0] = stream.toByteArray();
//                result[1] = "JPG";
//                break;
//        }
//        return result;
//    }

    /**
     * Refresh the text of the last movement item
     *
     * @param newText
     */
    public void refreshLastText(String newText) {
        if (mDrawMoveHistory.get(mDrawMoveHistory.size() - 1)
                .getDrawingMode() == DrawingMode.TEXT) {
            mDrawMoveHistory.get(mDrawMoveHistory.size() - 1).setText(newText);
            mDrawView.invalidate();
        } else {
//            Log.e(TAG, "The last item that you want to refresh text isn't TEXT element.");
        }

    }

    /**
     * Delete las history element, this can help for cancel the text request.
     */
    public void cancelTextRequest() {
        if (mDrawMoveHistory != null && mDrawMoveHistory.size() > 0) {
            mDrawMoveHistory.remove(mDrawMoveHistory.size() - 1);
            mDrawMoveHistoryIndex--;
        }
    }

    /**
     * getDrawAlpha绘制透明度
     *
     * @return mDrawAlpha透明度
     */
    public float getDrawAlpha() {
        return mDrawAlpha;
    }

    /**
     * getDrawColor方法
     *
     * @return mDrawColor绘制颜色
     */
    public int getDrawColor() {
        return mDrawColor;
    }

    /**
     * getDrawWidth获取绘制宽度
     *
     * @return mDrawWidth绘制宽度
     */
    public int getDrawWidth() {
        return mDrawWidth;
    }

    /**
     * getDrawingMode绘制模式方法
     *
     * @return mDrawingMode绘制模式
     */
    public DrawingMode getDrawingMode() {
        return mDrawingMode;
    }

    /**
     * getDrawingTool工具
     *
     * @return mDrawingTool对象
     */
    public DrawingTool getDrawingTool() {
        return mDrawingTool;
    }

    /**
     * getBackgroundColor获取背景颜色
     *
     * @return mBackgroundColor背景颜色
     */
    public int getBackgroundColor() {
        return mBackgroundColor;
    }

//    public Object getBackgroundImage() {
//        return mBackgroundImage;
//    }

    /**
     * getPaintStyle获取画笔样式
     *
     * @return mPaintStyle画笔样式
     */
    public SerializablePaint.Style getPaintStyle() {
        return mPaintStyle;
    }

    /**
     * getLineCap方法
     *
     * @return mLineCap对象
     */
    public SerializablePaint.StrokeCap getLineCap() {
        return mLineCap;
    }

    /**
     * getFontFamily获取字体
     *
     * @return mFontFamily字体
     */
    public Font getFontFamily() {
        return mFontFamily;
    }

    /**
     * getFontSize字体
     *
     * @return mFontSize字体大小
     */
    public float getFontSize() {
        return mFontSize;
    }

    /**
     * isAntiAlias方法
     *
     * @return mAntiAlias boolean值
     */
    public boolean isAntiAlias() {
        return mAntiAlias;
    }

    /**
     * isDither方法
     *
     * @return mDither 值
     */
    public boolean isDither() {
        return mDither;
    }

    /**
     * isZoomEnabled值
     *
     * @return mZoomEnabled值
     */
    public boolean isZoomEnabled() {
        return mZoomEnabled;
    }

    /**
     * isDrawViewEmpty方法
     *
     * @return mDrawMoveHistory.size()值
     */
    public boolean isDrawViewEmpty() {
        return mDrawMoveHistory == null || mDrawMoveHistory.size() == 0;
    }

    /**
     * isForCamera方法
     *
     * @return this.isForCamera
     */
    public boolean isForCamera() {
        return this.isForCamera;
    }

    /**
     * getMaxZoomFactor方法
     *
     * @return mMaxZoomFactor值
     */
    public float getMaxZoomFactor() {
        return mMaxZoomFactor;
    }

    /**
     * getZoomRegionScale方法
     *
     * @return mZoomRegionScale
     */
    public float getZoomRegionScale() {
        return mZoomRegionScale;
    }

    /**
     * getZoomRegionScaleMin方法
     *
     * @return mZoomRegionScaleMin
     */
    public float getZoomRegionScaleMin() {
        return mZoomRegionScaleMin;
    }

    /**
     * getZoomRegionScaleMax方法
     *
     * @return mZoomRegionScaleMax
     */
    public float getZoomRegionScaleMax() {
        return mZoomRegionScaleMax;
    }

    // SETTERS

    /**
     * Set the new draw parametters easily
     *
     * @param paint
     * @return this instance of the view
     */
    public DrawView refreshAttributes(SerializablePaint paint) {
        mDrawColor = paint.getColor().getValue();
        mPaintStyle = paint.getStyle();
        mDither = paint.getDither();
        mDrawWidth = (int) paint.getStrokeWidth();
        mDrawAlpha = paint.getAlpha();
        mAntiAlias = paint.isAntiAlias();
        mLineCap = paint.getStrokeCap();
        mFontFamily = paint.getFont();
        mFontSize = paint.getTextSize();
        return this;
    }

    /**
     * Set the current alpha value for the drawing
     *
     * @param drawAlpha
     * @return this instance of the view
     */
    public DrawView setDrawAlpha(float drawAlpha) {
        this.mDrawAlpha = drawAlpha;
        return this;
    }

    /**
     * Set the current draw color for drawing
     *
     * @param drawColor
     * @return this instance of the view
     */
    public DrawView setDrawColor(int drawColor) {
        this.mDrawColor = drawColor;
        return this;
    }

    /**
     * Set the current draw width
     *
     * @param drawWidth
     * @return this instance of the view
     */
    public DrawView setDrawWidth(int drawWidth) {
        this.mDrawWidth = drawWidth;
        return this;
    }

    /**
     * Set the current draw mode like draw, text or eraser
     *
     * @param drawingMode
     * @return this instance of the view
     */
    public DrawView setDrawingMode(DrawingMode drawingMode) {
        this.mDrawingMode = drawingMode;
        return this;
    }

    /**
     * Set the current draw tool like pen, line, circle, rectangle, circle
     *
     * @param drawingTool
     * @return this instance of the view
     */
    public DrawView setDrawingTool(DrawingTool drawingTool) {
        this.mDrawingTool = drawingTool;
        return this;
    }

    /**
     * Set the current background color of draw view
     *
     * @param backgroundColor
     * @return this instance of the view
     */
    public DrawView setBackgroundDrawColor(int backgroundColor) {
        this.mBackgroundColor = backgroundColor;
        return this;
    }

    /**
     * Set the current paint style like fill, fill_stroke or stroke
     *
     * @param paintStyle
     * @return this instance of the view
     */
    public DrawView setPaintStyle(SerializablePaint.Style paintStyle) {
        this.mPaintStyle = paintStyle;
        return this;
    }

    /**
     * Set the current line cap like round, square or butt
     *
     * @param lineCap
     * @return this instance of the view
     */
    public DrawView setLineCap(SerializablePaint.StrokeCap lineCap) {
        this.mLineCap = lineCap;
        return this;
    }

    /**
     * Set the current typeface for the view when we like to draw text
     *
     * @param fontFamily
     * @return this instance of the view
     */
    public DrawView setFontFamily(Font fontFamily) {
        this.mFontFamily = fontFamily;
        return this;
    }

    /**
     * Set the current font size for the view when we like to draw text
     *
     * @param fontSize
     * @return this instance of the view
     */
    public DrawView setFontSize(int fontSize) {
        this.mFontSize = fontSize;
        return this;
    }

    /**
     * Set the current anti alias value for the view
     *
     * @param antiAlias
     * @return this instance of the view
     */
    public DrawView setAntiAlias(boolean antiAlias) {
        this.mAntiAlias = antiAlias;
        return this;
    }

    /**
     * Set the current dither value for the view
     *
     * @param dither
     * @return this instance of the view
     */
    public DrawView setDither(boolean dither) {
        this.mDither = dither;
        return this;
    }

    /**
     * Enables the zoom
     *
     * @param zoomEnabled Value that indicates if the Zoom is enabled
     * @return this instance of the view
     */
    public DrawView setZoomEnabled(boolean zoomEnabled) {
        this.mZoomEnabled = zoomEnabled;
        return this;
    }

    /**
     * Set if the draw view is used for camera
     *
     * @param isForCamera Value that indicates if the draw view is for camera
     * @return this instance of the view
     */
    public DrawView setIsForCamera(boolean isForCamera) {
        this.isForCamera = isForCamera;
        return this;
    }

    /**
     * Set the customized background color for the view
     *
     * @param backgroundColor The background color for the view
     * @return this instance of the view
     */
    public DrawView setDrawViewBackgroundColor(int backgroundColor) {
        this.mBackgroundColor = backgroundColor;
        return this;
    }

    /**
     * Set the background paint for the view
     *
     * @param backgroundPaint The background paint for the view
     * @return this instance of the view
     */
    public DrawView setBackgroundPaint(SerializablePaint backgroundPaint) {
        this.mBackgroundPaint = backgroundPaint;
        return this;
    }

    /**
     * Set the background image for the DrawView. This image can be a File, Bitmap or ByteArray
     *
     * @param backgroundImage File that contains the background image
     * @param backgroundType  Background image type (File, Bitmap or ByteArray)
     * @param backgroundScale Background scale (Center crop, center inside, fit xy, fit top or fit bottom)
     * @return this instance of the view
     */
    public DrawView setBackgroundImage(PixelMap backgroundImage,
                                       BackgroundType backgroundType,
                                       BackgroundScale backgroundScale) {

        if (isForCamera) {
//            Log.i(TAG, "You can't set a background image if your draw view is for camera");
            return this;
        }

        if (onDrawViewListener != null)
            onDrawViewListener.onStartDrawing();

        if (mDrawMoveHistoryIndex >= -1 &&
                mDrawMoveHistoryIndex < mDrawMoveHistory.size() - 1)
            mDrawMoveHistory = mDrawMoveHistory.subList(0, mDrawMoveHistoryIndex + 1);

//        PixelMap bitmap = BitmapUtils.GetBitmapForDrawView(this, backgroundImage, backgroundType, 50);
        Matrix matrix = new Matrix();
        switch (backgroundScale) {
            case CENTER_CROP:
                matrix = MatrixUtils.GetCenterCropMatrix(new RectFloat(0, 0,
                                backgroundImage.getImageInfo().size.width,
                                backgroundImage.getImageInfo().size.height),
                        new RectFloat(0, 0, getWidth(), getHeight()));
                break;
            case CENTER_INSIDE:
                matrix.setRectToRect(new RectFloat(0, 0,
                                backgroundImage.getImageInfo().size.width,
                                backgroundImage.getImageInfo().size.height),
                        new RectFloat(0, 0, getWidth(), getHeight()), Matrix.ScaleToFit.CENTER);
                break;
            case FIT_XY:
                matrix.setRectToRect(new RectFloat(0, 0,
                                backgroundImage.getImageInfo().size.width,
                                backgroundImage.getImageInfo().size.height),
                        new RectFloat(0, 0, getWidth(), getHeight()), Matrix.ScaleToFit.FILL);
                break;
            case FIT_START:
                matrix.setRectToRect(new RectFloat(0, 0,
                                backgroundImage.getImageInfo().size.width,
                                backgroundImage.getImageInfo().size.height),
                        new RectFloat(0, 0, getWidth(), getHeight()), Matrix.ScaleToFit.START);
                break;
            case FIT_END:
                matrix.setRectToRect(new RectFloat(0, 0,
                                backgroundImage.getImageInfo().size.width,
                                backgroundImage.getImageInfo().size.height),
                        new RectFloat(0, 0, getWidth(), getHeight()), Matrix.ScaleToFit.END);
                break;
        }
//        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//        bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
//        byte[] bitmapArray = byteArrayOutputStream.toByteArray();
//        bitmap.recycle();

        mDrawMoveHistory.add(DrawMove.newInstance()
                .setBackgroundImage(backgroundImage, matrix)
                .setPaint(getNewPaintParams()));
        mDrawMoveHistoryIndex++;

        mDrawMoveBackgroundIndex = mDrawMoveHistoryIndex;

        if (onDrawViewListener != null)
            onDrawViewListener.onEndDrawing();

        invalidate();

        return this;
    }
//
//    /**
//     * Set the background image for the DrawView. This image can be a File, Bitmap or ByteArray
//     *
//     * @param backgroundImage  File that contains the background image
//     * @param backgroundType   Background image type (File, Bitmap or ByteArray)
//     * @param backgroundMatrix Background matrix for the image
//     * @return this instance of the view
//     */
//    public DrawView setBackgroundImage(Object backgroundImage,
//                                       BackgroundType backgroundType,
//                                       Matrix backgroundMatrix) {
//        if (!(backgroundImage instanceof File) && !(backgroundImage instanceof PixelMap) &&
//                !(backgroundImage instanceof byte[])) {
//            throw new RuntimeException("Background image must be File, Bitmap or ByteArray");
//        }
//
//        if (isForCamera) {
////            Log.i(TAG, "You can't set a background image if your draw view is for camera");
//            return this;
//        }
//
//        if (onDrawViewListener != null)
//            onDrawViewListener.onStartDrawing();
//
//        if (mDrawMoveHistoryIndex >= -1 &&
//                mDrawMoveHistoryIndex < mDrawMoveHistory.size() - 1)
//            mDrawMoveHistory = mDrawMoveHistory.subList(0, mDrawMoveHistoryIndex + 1);
//
//        PixelMap bitmap = BitmapUtils.GetBitmapForDrawView(this, backgroundImage, backgroundType, 50);
//        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//        bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
//        byte[] bitmapArray = byteArrayOutputStream.toByteArray();
//        bitmap.recycle();
//
//        mDrawMoveHistory.add(DrawMove.newInstance()
//                .setBackgroundImage(bitmapArray, backgroundMatrix)
//                .setPaint(new SerializablePaint()));
//        mDrawMoveHistoryIndex++;
//
//        mDrawMoveBackgroundIndex = mDrawMoveHistoryIndex;
//
//        if (onDrawViewListener != null)
//            onDrawViewListener.onEndDrawing();
//
//        invalidate();
//
//        return this;
//    }

    /**
     * Set the max zoom factor of the DrawView
     *
     * @param maxZoomFactor The max zoom factor target
     * @return this instance of the view
     */
    public DrawView setMaxZoomFactor(float maxZoomFactor) {
        this.mMaxZoomFactor = maxZoomFactor;
        return this;
    }

    /**
     * Sets the ZoomRegionView scale factor
     *
     * @param zoomRegionScale ZoomRegionView scale factor (DrawView size / scale)
     * @return this instance of the view
     */
    public DrawView setZoomRegionScale(float zoomRegionScale) {
        this.mZoomRegionScale = zoomRegionScale;
        return this;
    }

    /**
     * Sets the ZoomRegionView minimum scale factor
     *
     * @param zoomRegionScaleMin ZoomRegionView scale factor minimum
     * @return this instance of the view
     */
    public DrawView setZoomRegionScaleMin(float zoomRegionScaleMin) {
        this.mZoomRegionScaleMin = zoomRegionScaleMin;
        return this;
    }

    /**
     * Sets the ZoomRegionView maximum scale factor
     *
     * @param zoomRegionScaleMax ZoomRegionView scale factor maximum
     * @return this instance of view
     */
    public DrawView setZoomRegionScaleMax(float zoomRegionScaleMax) {
        this.mZoomRegionScaleMax = zoomRegionScaleMax;
        return this;
    }

    // PRIVATE METHODS

//    @Override
//    protected int[] onCreateDrawableState(int extraSpace) {
//        return super.onCreateDrawableState(extraSpace);
//    }

    /**
     * Draw the background image on DrawViewCanvas
     *
     * @param drawMove the DrawMove that contains the background image
     * @param canvas   tha DrawView canvas
     */
    private void drawBackgroundImage(DrawMove drawMove, Canvas canvas) {
        canvas.save();
        canvas.concat(drawMove.getBackgroundMatrix());
        canvas.drawPixelMapHolder(new PixelMapHolder(drawMove.getBackgroundImage()), 0, 0, new Paint());
        canvas.restore();
    }

    /**
     * Shows or hides ZoomRegionView
     *
     * @param visibility the ZoomRegionView visibility target
     */
    private void showHideZoomRegionView(final int visibility) {
        if (mZoomRegionView.getTag() == null) {
            int alpha = -1;

            if (visibility == VISIBLE && mZoomRegionView.getVisibility() == INVISIBLE){
                alpha = 1;
            }
            else if (visibility == INVISIBLE && mZoomRegionView.getVisibility() == VISIBLE) {
                alpha = 0;
            }

            if(alpha != -1) {
                mZoomRegionView.createAnimatorProperty()
                        .setDuration(300)
                        .setCurveType(Animator.CurveType.ACCELERATE_DECELERATE)
                        .alpha(alpha)
                        .setStateChangedListener(new Animator.StateChangedListener() {
                            @Override
                            public void onStart(Animator animator) {
                                if (visibility == VISIBLE)
                                    mZoomRegionView.setVisibility(VISIBLE);
                            }

                            @Override
                            public void onStop(Animator animator) {

                            }

                            @Override
                            public void onCancel(Animator animator) {

                            }

                            @Override
                            public void onEnd(Animator animator) {
                                if (visibility == INVISIBLE)
                                    mZoomRegionView.setVisibility(INVISIBLE);
                                mZoomRegionView.setTag(null);
                            }

                            @Override
                            public void onPause(Animator animator) {

                            }

                            @Override
                            public void onResume(Animator animator) {

                            }
                        }).start();
            }
        }
    }

    // SCALE
    public class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            if (mZoomEnabled) {
                mFromZoomRegion = false;
                mZoomFactor = detector.getScale();
                mZoomFactor = Math.max(1f, Math.min(mZoomFactor, mMaxZoomFactor));
                mZoomFactor = mZoomFactor > mMaxZoomFactor ? mMaxZoomFactor : mZoomFactor < 1f ? 1f : mZoomFactor;
                mZoomCenterX = detector.getFocusX() / mZoomFactor + mCanvasClipBounds.left;
                mZoomCenterY = detector.getFocusY() / mZoomFactor + mCanvasClipBounds.top;
                if (mZoomFactor > 1f) {
                    showHideZoomRegionView(VISIBLE);
                }
                else
                    showHideZoomRegionView(INVISIBLE);
                mDrawView.invalidate();
            }
            return false;
        }

    }

    /**
     * onDoubleTap方法
     *
     * @param touchEvent 按键事件
     * @return boolean类型
     */
    public boolean onDoubleTap(TouchEvent touchEvent) {
        if (mZoomEnabled) {
            mFromZoomRegion = false;
            int animationOption = -1;

            if (mZoomFactor >= 1f && mZoomFactor < mMaxZoomFactor)
                animationOption = 0;
            else if (mZoomFactor <= mMaxZoomFactor && mZoomFactor > 1f)
                animationOption = 1;

            if (animationOption != -1) {
                AnimatorValue animatorValue = new AnimatorValue();
                final float start = mZoomFactor;
                float temp;
                if (animationOption == 0) {
                    temp = mMaxZoomFactor;
                }
                else {
                    float distance = mMaxZoomFactor - mZoomFactor;
                    temp = distance;
                }
                final float end = temp;
                animatorValue.setDuration(300);
                animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
                animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
                    @Override
                    public void onUpdate(AnimatorValue animatorValue, float v) {
                        mZoomFactor = start + v * (end - start);
                        mZoomFactor = mZoomFactor < 1f ? 1 : mZoomFactor;
                        mZoomCenterX = touchEvent.getPointerPosition(0).getX()/ mZoomFactor + mCanvasClipBounds.left;
                        mZoomCenterY = touchEvent.getPointerPosition(0).getY()/ mZoomFactor + mCanvasClipBounds.top;
                        if (mZoomFactor > 1f) {
                            mZoomRegionView.setAlpha(1);
                            mZoomRegionView.setVisibility(VISIBLE);
                        }
                        else
                            mZoomRegionView.setVisibility(INVISIBLE);
                        mDrawView.invalidate();
                    }
                });
                animatorValue.setStateChangedListener(new Animator.StateChangedListener() {
                    @Override
                    public void onStart(Animator animator) {
                        isDoubleEvent = true;
                    }

                    @Override
                    public void onStop(Animator animator) {

                    }

                    @Override
                    public void onCancel(Animator animator) {
                        isDoubleEvent = false;
                    }

                    @Override
                    public void onEnd(Animator animator) {
                        isDoubleEvent = false;
                    }

                    @Override
                    public void onPause(Animator animator) {

                    }

                    @Override
                    public void onResume(Animator animator) {

                    }
                });
                animatorValue.start();
            }
        }
        return true;
    }

    // LISTENER

    /**
     * Setting new OnDrawViewListener for this view
     *
     * @param onDrawViewListener
     */
    public void setOnDrawViewListener(OnDrawViewListener onDrawViewListener) {
        this.onDrawViewListener = onDrawViewListener;
    }

    /**
     * OnDrawViewListener 接口Listener for registering drawing actions of the view
     *
     * @since 2021-08-06
     */
    public interface OnDrawViewListener {
        /**
         * onStartDrawing 方法
         */
        void onStartDrawing();

        /**
         * onEndDrawing
         */
        void onEndDrawing();
        /**
         * onClearDrawing
         */
        void onClearDrawing();
        /**
         * onRequestText
         */
        void onRequestText();
        /**
         * onAllMovesPainted
         */
        void onAllMovesPainted();
    }
}
